package cn.icanci.loopstack.rec.spi.event;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.function.Consumer;

/**
 * 事件分发器默认实现
 *
 * @author icanci
 * @since 1.0 Created in 2022/11/11 18:01
 */
public class DefaultEventDispatcher extends AbstractEventDispatcher {

    /**
     * 注册事件和监听器
     */
    @Override
    @SuppressWarnings("all")
    protected void register() {
        Map<String, BaseEvent> eventMap = applicationContext.getBeansOfType(BaseEvent.class);
        for (BaseEvent bean : eventMap.values()) {
            eventClasses.add(bean.getClass());
        }

        Map<String, BaseEventListener> listenerMap = applicationContext.getBeansOfType(BaseEventListener.class);
        for (BaseEventListener bean : listenerMap.values()) {
            listeners.add(bean);
        }
        postEventListenerMapper();
    }

    /**
     * 映射mapper
     */
    private void postEventListenerMapper() {
        if (listeners.isEmpty() || eventClasses.isEmpty()) {
            return;
        }

        for (Class<?> eventClass : eventClasses) {
            for (BaseEventListener listener : listeners) {
                if (!isSupport(eventClass, listener)) {
                    continue;
                }
                List<BaseEventListener> baseEventListeners = eventListMap.get(eventClass);
                if (baseEventListeners == null || baseEventListeners.isEmpty()) {
                    baseEventListeners = new ArrayList<>();
                }
                baseEventListeners.add(listener);
                eventListMap.put(eventClass, baseEventListeners);
            }
        }

        // 清空 help gc
        listeners = null;
        eventClasses = null;

        // 排序
        Collection<List<BaseEventListener>> listCollection = eventListMap.values();
        for (List<BaseEventListener> baseEventListeners : listCollection) {
            baseEventListeners.sort(LISTENER_COMPARATOR);
        }
    }

    /**
     * 是否支持加入到listener
     *
     * @param eventClass eventClass
     * @param listener listener
     * @return 是否支持加入到listener
     */
    private boolean isSupport(Class<?> eventClass, BaseEventListener listener) {
        Type type = listener.getClass().getGenericSuperclass();
        ParameterizedType parameterizedType = (ParameterizedType) type;
        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
        Type actualTypeArgument = actualTypeArguments[0];
        return eventClass == actualTypeArgument;
    }

    /**
     * 移除监听器
     *
     * @param baseEvent baseEvent 事件
     * @param baseEventListener baseEventListener 监听器
     */
    public void remove(BaseEvent baseEvent, BaseEventListener baseEventListener) {
        List<BaseEventListener> listeners = eventListMap.get(baseEvent.getClass());
        if (listeners == null || listeners.isEmpty()) {
            return;
        }
        listeners.remove(baseEventListener);
    }

    /**
     * 分发事件
     * 
     * @param baseEvent baseEvent
     */
    @Override
    public void fire(BaseEvent baseEvent) {
        fire(baseEvent, true);
    }

    /**
     * 分发事件
     *
     * @param baseEvent baseEvent
     * @param sync 是否同步发送
     */
    @Override
    public void fire(final BaseEvent baseEvent, boolean sync) {
        final List<BaseEventListener> listeners = eventListMap.get(baseEvent.getClass());
        if (listeners == null || listeners.isEmpty()) {
            return;
        }

        Consumer<List<BaseEventListener>> consumer = sync ? baseEventListeners -> {
            for (final BaseEventListener listener : baseEventListeners) {
                listener.onEvent(baseEvent);
            }
        } : baseEventListeners -> {
            Executor taskExecutor = getTaskPool();
            for (final BaseEventListener listener : baseEventListeners) {
                taskExecutor.execute(new EventHandler(baseEvent, listener));
            }
        };

        consumer.accept(listeners);
    }

}
